﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Core.Interfaces;
using UnityEngine;

namespace Core
{
    /// <summary>
    /// 事件系统
    /// PS:待增加事件管理机制（自动卸载、销毁）
    /// </summary>
    public class EventSys : ICancelToken
    {
        private sealed class CancleTokenActionWrap
        {
            public CancellationToken cancellationToken { get; private set; }
            public Action<ITuple> CallBack { get; private set; }
            public CancleTokenActionWrap(Action<ITuple> action, CancellationToken cancellation)
            {
                cancellationToken = cancellation;
                CallBack = action;
            }
        }

        private Dictionary<ushort, List<CancleTokenActionWrap>> notifyActions =
            new Dictionary<ushort, List<CancleTokenActionWrap>>();


        /// <summary>
        /// 注册
        /// </summary>
        /// <param name="notification"></param>
        /// <param name="action"></param>
        /// <param name="cancellation"></param>
        public void RegHandler(Enum notification, Action<ITuple> action, CancellationToken cancellation)
        {
            ushort code = (ushort) (notification as ValueType);
            if (!notifyActions.ContainsKey(code))
            {
                notifyActions.Add(code, new List<CancleTokenActionWrap>());
            }

            var handlers = notifyActions[code];
            if (!TryGetActionWrap(handlers, action, out var cancelTokenActionWrap))
            {
                cancelTokenActionWrap = new CancleTokenActionWrap(action, cancellation);
                handlers.Add(cancelTokenActionWrap);
            }
        }

        /// <summary>
        /// 注销该通知下所有事件
        /// </summary>
        /// <param name="notification"></param>
        public void UnRegHandler(Enum notification)
        {
            ushort code = (ushort) (notification as ValueType);
            if (notifyActions.TryGetValue(code, out var handlers))
            {
                notifyActions.Remove(code);
            }
        }

        /// <summary>
        /// 注销该通知下指定事件
        /// </summary>
        /// <param name="notification"></param>
        /// <param name="action"></param>
        public void UnRegHandler(Enum notification, Action<ITuple> action)
        {
            ushort code = (ushort) (notification as ValueType);
            if (notifyActions.TryGetValue(code, out var handlers))
            {
                for (int i = 0; i < handlers.Count; i++)
                {
                    var handlerWrap = handlers[i];
                    if (handlerWrap.CallBack != null && handlerWrap.CallBack == action)
                    {
                        handlers.Remove(handlerWrap);
                        break;
                    }
                }
            }
        }

        private bool TryGetActionWrap(List<CancleTokenActionWrap> handlers, Action<ITuple> action,
            out CancleTokenActionWrap cancelTokenActionWrap)
        {
            bool ret = false;
            cancelTokenActionWrap = null;
            foreach (var item in handlers)
            {
                if (item.CallBack != null && item.CallBack == action)
                {
                    ret = true;
                    cancelTokenActionWrap = item;
                    break;
                }
            }

            return ret;
        }

        /// <summary>
        /// 通知
        /// </summary>
        /// <param name="notification"></param>
        /// <param name="msg"></param>
        public void Notify(Enum notification, ITuple msg = null)
        {
            ushort code = (ushort) (notification as ValueType);
            if (notifyActions.TryGetValue(code, out var handlers))
            {
                for (int i = 0; i < handlers.Count; i++)
                {
                    handlers[i]?.CallBack?.Invoke(msg);
                }
            }
        }

        private List<ushort> clearKeys = new List<ushort>();

        public void OnCancelToken()
        {
            clearKeys.Clear();
            foreach (var item in notifyActions.Keys)
            {
                var handlerWraps = notifyActions[item];
                for (int i = 0; i < handlerWraps.Count; i++)
                {
                    var handlerWrap = handlerWraps[i];
                    if (handlerWrap.cancellationToken.IsCancellationRequested)
                    {
                        handlerWraps.Remove(handlerWrap);
                    }
                }

                if (handlerWraps.Count == 0)
                {
                    clearKeys.Add(item);
                }
            }

            foreach (var item in clearKeys)
            {
                notifyActions.Remove(item);
            }

            clearKeys.Clear();
        }
    }
}